<template>
  <article class="page page-wxdat-decrypt">
    <h4 class="text-center">微信DAT文件解码</h4>
    <form class="form">
      <div class="form-group">
        <fieldset>
          <legend>解码设置</legend>
          <div class="alert alert-info" role="alert">
            <div>注：因微信DAT文件绝大多数为图片文件，在考虑运算性能的前提下，本程序目前仅支持对图片文件进行解码。</div>
          </div>
          <div class="row">
            <div class="col col-6">
              <div class="row-flex" align="center">
                <div class="row-flex-prefix">下载位置</div>
                <div class="row-flex-main"><input class="form-control" type="text" v-model="toPath" /></div>
                <div class="row-flex-suffix"><x-button @click="setToPath">选择...</x-button></div>
              </div>
            </div>
            <div class="col col-6 row-flex" align="center">
              <div class="form-check">
                <input type="checkbox" class="form-check-input" v-model="sameTime">
                <label class="form-check-label">
                  解码后访问时间&更改时间与原文件保持一致
                </label>
              </div>
            </div>
          </div>
        </fieldset>
      </div>
      <div class="form-group file-list-wrapper">
        <fieldset>
          <legend>文件列表</legend>
          <div class="file-list" @drop.prevent="drop" @dragover.prevent >
            <div class="file-item" v-for="(v,i) in filesList" :key="v.path">
              <div class="file-item-header">
                <div class="file-item-name text-ellipsis" :title="v.name">{{v.name}}</div>
                <div class="file-item-delete" @click="fileItemDelete(i)"><i class="fa fa-times"></i></div>
              </div>
              <div class="file-item-body">
                <div class="file-item-thumbnail">
                  <img :src="v.url" />
                </div>
                <div class="file-item-text">
                  <p><span class="file-item-label">文件类型：</span><span class="file-item-value text-ellipsis">{{v.type}}</span></p>
                  <p><span class="file-item-label">文件大小：</span><span class="file-item-value text-ellipsis">{{v.size | formatFileSize}}</span></p>
                  <p><span class="file-item-label">执行操作：</span><span class="file-item-value">
                    <a class="btn btn-link" @click="saveSingleFile(i)">下载文件</a>
                  </span></p>
                </div>
              </div>
            </div>
            <div v-show="filesList.length===0" class="nodata">将待处理文件拖动到此处</div>
          </div>
        </fieldset>
      </div>
      <div class="form-group text-center">
        <x-button type="default" @click="deleteAll">全部删除</x-button>
        <x-button type="primary" @click="saveAll">全部下载</x-button>
      </div>
    </form>
  </article>
</template>

<script>
const fs = global.require('fs')
const path = global.require('path')
const {remote} = global.require('electron')
const {dialog} = remote
const selfWindow= remote.getCurrentWindow()
let {formatFileSize}  = global.$jstools || {}

export default {
  name: 'wxdat-decrypt',
  data () {
    return {
      toPath: this.$sharedObject('desktopPath'),
      sameTime: false,
      filesList: [],
      fileHeaderMarks: {
        'jpg': [0xFF, 0xD8, 0xFF],
        'png': [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A],
        'gif': [0x47, 0x49, 0x46, 0x38, 0x39, 0x61],
        'bmp': [0x42, 0x4D],
        'pcx': [0x0A, 0x05, 0x01, 0x08],
        'webp': [0x52, 0x49, 0x46, 0x46],
        'tif|tiff': [0x49, 0x49, 0x2A, 0x00],
        'TIF|TIFF': [0x4D, 0x4D, 0x00, 0x2A],
        'IFF': [0x46, 0x4F, 0x52, 0x4D],
        'ANI': [0x52, 0x49, 0x46, 0x46],
        'tga|ico|cur': [0x00, 0x00, 0x02, 0x00, 0x00],
        'tga': [0x00, 0x00, 0x02, 0x00, 0x00],
        'ico': [0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x20, 0x20],
        'CUR': [0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x20, 0x20],
      },
      fileMimeType: {
        'jpg': 'image/jpeg',
        'png': 'image/png',
        'gif': 'image/gif',
        'bmp': 'image/bmp',
        'pcx': 'image/jpeg',
        'webp': 'image/webp',
        'tif|tiff': 'image/tif',
        'TIF|TIFF': 'image/tif',
        'IFF': 'image/iff',
        'ANI': 'image/ani',
        'tga|ico|cur': 'image/x-icon',
        'tga': 'image/x-icon',
        'ico': 'image/x-icon',
        'CUR': 'image/x-icon',
      }
    }
  },
  filters: {
    formatFileSize (val) {
      return formatFileSize(val)
    }
  },
  mounted () {
    window.abc = this
  },
  methods: {
    // 设置下载位置
    setToPath () {
      let filePaths = dialog.showOpenDialogSync(selfWindow, {
        title: '选择目录',
        buttonLabel:'确定',
        defaultPath: this.$sharedObject('desktopPath'),
        properties: ['openDirectory']
      })
      if (filePaths instanceof Array && filePaths.length > 0) {
        this.toPath = filePaths[0]
      }
    },
    // 拖拽添加文件
    drop (e) {
      const fileList = e.dataTransfer.files
      if (!fileList) return
      // 取文件后缀名，必须是dat文件
      let taskList = []
      let indexList = []
      let fStatList = []
      for (let i = 0; i < fileList.length; i++) {
        const file = fileList[i]
        if (!/.dat$/i.test(file.name)) {
          return this.$toast('请选择DAT文件！')
        }
        let fStat = fs.statSync(file.path)
        if (fStat.isDirectory()) {
          return this.$toast('请选择DAT文件！')
        }
        taskList.push(this.getFileInfo(file.path))
        indexList.push(i)
        fStatList.push(fStat)
      }
      Promise.all(taskList).then((infos) => {
        if (!infos || !infos.length) return
        infos.forEach((v, i) => {
          let file = fileList[indexList[i]]
          let fStat = fStatList[indexList[i]]
          this.filesList.push({
            name: file.name, path: file.path, size: file.size,
            atime: fStat.atime, mtime: fStat.mtime, ...v
          })
        })
      })
    },
    // 获取文件信息
    getFileInfo (filePath) {
      let result = {name: '', type: '未知', password: '', data: '', url: ''}
      let _arr = path.basename(filePath).split('.')
      result.name = _arr.slice(0, _arr.length - 1).join('.')
      return new Promise((resolve, reject) => {
        if (!fs.existsSync(filePath)) {
          reject('文件不存在')
        }
        fs.readFile(filePath, (err, data) => {
          if (err || !data) reject('读取文件错误')
          result.data = data
          for (let key in this.fileHeaderMarks) {
            let val = this.fileHeaderMarks[key] || []
            let pwd1 = (val[0] || 0) ^ data[0]
            let pwd2 = (val[1] || 0) ^ data[1]
            if (pwd1 === pwd2) {
              result.type = key
              result.password = pwd1
              break
            }
          }
          if (result.password) {
            let mimeType = this.fileMimeType[result.type] || `image/${result.type}`
            result.data = result.data.map(v => (v ^ result.password))
            result.url = `data:${mimeType};base64,${result.data.toString('base64')}`
          }
          resolve(result)
        })
      })
    },
    // 删除文件
    fileItemDelete (index) {
      this.filesList.splice(index, 1)
    },
    // 下载文件
    saveFile (index) {
      if (!this.toPath) {
        this.$toast('请设置文件下载位置')
        return
      }
      let item = this.filesList[index] || {}
      let fileName = `${item.name}.${(item.type || '').split('|')[0]}`
      let filePath = path.join(this.toPath, fileName)
      return this.$sharedObject('jstoolsNode').writeFile(filePath, item.data).then(() => {
        if (this.sameTime) {
          fs.utimesSync(filePath, item.atime, item.mtime)
        }
      })
    },
    // 下载单个文件
    saveSingleFile (index) {
      this.saveFile(index).then(() => {
        this.$toast('文件下载成功')
      }).catch((err) => {
        this.$toast('文件下载失败')
        console.log('saveSingleFile:', err)
      })
    },
    // 全部下载
    saveAll () {
      if (!this.toPath) {
        this.$toast('请设置文件下载位置')
        return
      }
      if (this.filesList.length <= 0) {
        this.$toast('未找到可处理的文件')
        return
      }
      let taskList = []
      this.filesList.forEach((v, i) => {
        taskList.push(this.saveFile(i))
      })
      Promise.all(taskList).then(() => {
        this.$toast('文件下载成功')
      }).catch((err) => {
        this.$toast('文件下载失败')
        console.log('saveAll:', err)
      })
    },
    // 全部删除
    deleteAll () {
      this.filesList = []
    }
  }
}
</script>

<style scoped lang="less">
@import "~@/assets/css/_mixins.less";
@import "~@/assets/css/_color.less";
.row-flex {
  & + .row-flex {
    margin-top: .5em;
  }
  .row-flex-prefix {
    width: 4.5em;
    text-align: left;
  }
}
.form {
  height: calc(100vh - 137px);
  display: flex;
  flex-direction: column;
  .form-group {
    fieldset {
      height: 100%;
    }
  }
  .alert-info {
    margin-bottom: 1em;
  }
  .file-list-wrapper {
    height: 0;
    flex-grow: 1;
    .file-list {
      height: calc(100% - 2em);
      overflow-y: auto;
      display: flex;
      flex-wrap: wrap;
      position: relative;
      .file-item {
        width: 380px;
        height: calc(170px + 3em);
        margin: .5em auto;
        border-radius: 8px;
        background-color: #fff;
        box-shadow: 0 0 .5em @borderGrey;
        .file-item-header {
          padding: .5em;
          display: flex;
          border-bottom: 2px solid @borderGrey;
          .file-item-name {
            width: 0;
            flex-grow: 1;
          }
          .file-item-delete {
            .flex-center;
            cursor: pointer;
            .fa-times {
              font-size: 1em;
            }
          }
        }
        .file-item-body {
          height: calc(160px + 1em);
          padding: .5em;
          display: flex;
          .file-item-thumbnail {
            width: 150px;
            margin-right: .5em;
            border: 1px solid @bgGrey;
            background-color: @bgGrey;
            img {
              display: block;
              width: 100%;
              height: 100%;
              object-fit: contain;
            }
          }
          .file-item-text {
            width: 0;
            flex-grow: 1;
            p {
              margin-bottom: 0;
              height: 30px;
              line-height: 30px;
              display: flex;
              .file-item-label {}
              .file-item-value {
                width: 0;
                flex-grow: 1;
              }
            }
            .btn-link {
              padding: 0;
            }
          }
        }
      }
    }
  }
}
</style>
